package com.lolaage.tracks;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.os.Binder;
import android.os.IBinder;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.util.Log;

import com.lolaage.tracks.MyLocationPrivider.OnLocationListener;
import com.lolaage.tracks.MyTrackManager.LOC_ENGINER;
import com.lolaage.tracks.MyTrackManager.TRACK_STATE;
import com.lolaage.tracks.adapter.AdaptiveLocationPolicy;
import com.lolaage.tracks.adapter.LastLocationValider;
import com.lolaage.tracks.adapter.MyBMapManager;
import com.lolaage.tracks.adapter.MyLocationManager;
import com.lolaage.tracks.content.TrackProvider;
import com.lolaage.tracks.util.LocationUtils;

/*********************************************************
 * @author : zhaohua
 * @version : 2012-9-10
 * @see : 轨迹记录服务
 * @Copyright : copyrights reserved by personal 2007-2011
 **********************************************************/
public class MyTrackService extends Service
{   
    private static final String TAG = "MyTrackService";
    private ExecutorService executorService;
    private TrackProvider mTrackProder;
    private MyLocationPrivider myLocationManager;
    private TrackListener mTrackListner;
    private Context mContext;
    private TRACK_STATE mState = TRACK_STATE.STOPED;
    /** 通知参数*/
    private Track    mCurTrack;
    private Location mLastValidLocation;
    private Location mPreLocation;
    private Location mCurLocation;
    private final Location SEGMENT_LOCATION;
    // 初始通知间隔时间
    private int      mInitNotifyTime = 100000;
    /** 最小记录距离*/
    private int      mMinRecordingDist = 10;
    /** 最大记录距离*/
    private int      mMaxRecordingDist = 200;
    
    public MyTrackService(int clientId,Context context)
    {
        mContext = context;
        initCilent(clientId);
        SEGMENT_LOCATION = new Location("segment");
        SEGMENT_LOCATION.setLatitude(0);
        SEGMENT_LOCATION.setLongitude(0);
    }
    
    public MyTrackService()
    {
        SEGMENT_LOCATION = new Location("segment");
        SEGMENT_LOCATION.setLatitude(0);
        SEGMENT_LOCATION.setLongitude(0);
    }
    
    public void initCilent(int clientId)
    {
        mTrackProder = new TrackProvider(clientId);
        initSelfLocEnginer(true);      
    }
    
    public void initSelfLocEnginer(boolean initable)
    {
        if(myLocationManager != null && myLocationManager instanceof MyLocationManager)
        {
            return;
        }
        if(!initable)
        {
            pause();
        }
        myLocationManager = MyLocationManager.getInstace(mContext);
        initLocOption();
        if(!initable)
        {
            resume();
        }
    }
    
    public void initBaiduLocEnginer(boolean initable)
    {        
        if(myLocationManager != null && myLocationManager instanceof MyBMapManager)
        {
            return;
        }
        if(!initable)
        {
            pause();
        }
        if(myLocationManager instanceof MyLocationManager)
        {
            myLocationManager.destory();           
        }
        myLocationManager = MyBMapManager.getInstace(mContext);
        initLocOption();
        if(!initable)
        {
            resume();
        }
    }
    
    public void initLocOption()
    {
        // 设置定位的优先级
        myLocationManager.setPrivity(MyLocationPrivider.L_GPS_FIRST);
        // 设置定位的位置判定者
        myLocationManager.setLocationValider(new LastLocationValider());
        // 设置定位的策略适配器: 最小10s，最大300s，最小10m，最大50m
        myLocationManager.setLocationPolicy(new AdaptiveLocationPolicy(10000,300000,10,50));
    }
    
    public LOC_ENGINER getLocEnginerType()
    {
        if(myLocationManager != null)
        {
            if(myLocationManager instanceof MyLocationManager)
            {
                return LOC_ENGINER.MYSLEF;
            }else
            {
                return LOC_ENGINER.BAIDU;
            }
        }else
        {
            return LOC_ENGINER.NONE;
        }
    }
    
    /** 接着最后一个轨迹进行*/
    public void startContinue()
    {
        mCurTrack = mTrackProder.getLastTrack();
        start();
    }
    
    public long getCurTrackId()
    {
        return mCurTrack != null ? mCurTrack._id : 0;
    }
    
    /** 开始一个全新的轨迹*/
    public void start()
    {        
        reset();
        if(mCurTrack == null)
        {
            // 新开始的
            mCurTrack = mTrackProder.newTrack();
            if(mTrackListner != null)
            {
                mTrackListner.onNewTrack(mCurTrack);
            }
        }else 
        {
            // 继续的,获取之前最后的有效的一个轨迹节点
            TrackPoint lastPoint = mTrackProder.getLastPoint(mCurTrack.endPid);
            if(lastPoint != null)
            {
                mPreLocation = lastPoint.toLocation();
                mLastValidLocation = mPreLocation;
            }
        }
        if(executorService == null || executorService.isShutdown() || executorService.isTerminated())
        {
            executorService = Executors.newSingleThreadExecutor();
        }
        resume();
    }
    
    public void pause()
    {        
        myLocationManager.rmvLocationListener(mListener);
        myLocationManager.stopKeepLocation();       
        mState = TRACK_STATE.PAUSEED;
        releaseWakeLock();
        showNotification();
    }
    
    public void resume()
    {
        acquireWakeLock();
        myLocationManager.addLocationListener(mListener);
        myLocationManager.startKeepLocation(mInitNotifyTime);                       
        mState = TRACK_STATE.STARTED;
        showNotification();
    }
    
    public void stop()
    {
        pause();       
        if(executorService != null && !executorService.isShutdown())
        {
            executorService.shutdown();
        }
        reset();
        mCurTrack = null;       
        mState = TRACK_STATE.STOPED;
    }
    
    public void destory()
    {
        stop();
        myLocationManager.destory();
    }    
    
    private void reset()
    {
        mLastValidLocation = null;
        mPreLocation = null;
        mCurLocation = null;
        mState = TRACK_STATE.STOPED;
    }
    
    public TRACK_STATE getState()
    {
        return mState;
    }
    
    public void addTrackListener(TrackListener listner)
    {
        mTrackListner = listner; 
    }
    
    public void rmvTrackListener(TrackListener listner)
    {
        mTrackListner = null;
    }
    
    /** 位置检核 */
    private synchronized boolean checkLocation(Location location)
    {
        if(!LocationUtils.isValidLocation(location)) return false;
        
        if(mLastValidLocation == null)
        {
            // 初始点
        }else
        if(mLastValidLocation != null && location != null)
        {
            // 实时改变 最小通知时间与距离
            int time = Math.abs((int)(location.getTime() - mLastValidLocation.getTime()));
            int dist = (int)location.distanceTo(mLastValidLocation);
            Log.d(TAG, "--->check time = " + time + " : dist = " + dist + "; v = " + dist/(time+1));
            myLocationManager.setLocationOption(time, dist);
            // 是否符合最小记录距离
            int distDetal = (int)location.distanceTo(mLastValidLocation);
            if( distDetal < mMinRecordingDist)
            {
                return false;
            }
            // 是否符合最大合法距离,超过则设为断点
            if(distDetal > mMaxRecordingDist)
            {
                // 不合法则此点设为断点
                mPreLocation = mLastValidLocation;
                mLastValidLocation = location;
                mCurLocation = SEGMENT_LOCATION;
                return true;
            }
        }
        // 合法则为当前, 交换        
        mPreLocation = mLastValidLocation;
        mLastValidLocation = location;
        mCurLocation = location;
        return true;
    }
    
    /** 处理位置改变时*/
    private void onLocationChangedAsyn(Location preLocation, Location lastLocation, Location curLoction)
    {
        if (curLoction != null && mCurTrack != null)
        {
            TrackPoint point = TrackPoint.toTrackPoint(curLoction);
            TrackPoint lastPoint = null;
            boolean isSegment = curLoction == SEGMENT_LOCATION;
            if (preLocation != null && !isSegment)
            {
                mCurTrack.totalDistance += curLoction.distanceTo(preLocation);
            }
            // 保存节点到当前轨迹
            if (mTrackProder.addPoint(mCurTrack, point))
            {
                // 是否有断点，则追加一个合法的位置
                if (isSegment)
                {
                    lastPoint = TrackPoint.toTrackPoint(lastLocation);
                    mTrackProder.addPoint(mCurTrack, lastPoint);
                }
                if (mTrackListner != null)
                {
                    List<TrackPoint> list = new ArrayList<TrackPoint>(2);
                    list.add(point);
                    if (lastPoint != null)
                    {
                        list.add(lastPoint);
                    }
                    try
                    {
                        mTrackListner.onTrackPointChanged(list);
                    } catch (Exception e)
                    {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    /** 位置监听器*/
    private OnLocationListener mListener = new OnLocationListener()
    {
        @Override
        public void onLocationChanged(Location location)
        {
            if(location == null || mState != TRACK_STATE.STARTED) return;
            Log.d(TAG, "--->location reached " + location.getTime());
            // 进行位置检验
            if(checkLocation(location))
            {
                if(executorService == null || executorService.isShutdown() || executorService.isTerminated())
                {
                    return;
                }
                executorService.submit(new OnLocationRunnale(mPreLocation, mLastValidLocation, mCurLocation));
            }
        }

        @Override
        public void onLocationError()
        {
            
        }
    };

    /** 位置处理任务*/
    private class OnLocationRunnale implements Runnable
    {
        private Location preLocation;
        private Location lastLocation;
        private Location curLoction;
        
        public OnLocationRunnale(Location preLocation,Location lastLocation,Location curLoction)
        {
            this.preLocation = preLocation;
            this.lastLocation = lastLocation;
            this.curLoction = curLoction;
        }
        
        @Override
        public void run()
        {
            onLocationChangedAsyn(preLocation, lastLocation, curLoction);
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////
    private WakeLock wakeLock;
    private final IBinder mBinder = new LocalBinder(); 
    
    public void onCreate() 
    {
        mContext = this;
        
        Log.d(TAG, "onCreate");
    };
    
    @Override
    public void onDestroy()
    {
        destory();
        Log.d(TAG, "onDestroy");
        super.onDestroy();
    }
    
    @Override
    public void onStart(Intent intent, int startId)
    {
        handleStartCommand(intent, startId);
        Log.d(TAG, "onStart");
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId)
    {
        handleStartCommand(intent, startId);
        return START_STICKY;
    }
    
    @Override
    public IBinder onBind(Intent intent)
    {
        return mBinder;
    }
    
    /** 
     * Class for clients to access.    Because we know this service always 
     * runs in the same process as its clients, we don't need to deal with 
     * IPC. 
     */ 
    public class LocalBinder extends Binder 
    { 
        MyTrackService getService()
       { 
            return MyTrackService.this; 
       } 
    } 
    
    private void handleStartCommand(Intent intent, int startId)
    {
        // Check if the service is called to resume track (from phone reboot)        
    }
    
    /**
     * Acquires the wake lock.
     */
    private void acquireWakeLock()
    {
        try
        {
            PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
            if (powerManager == null)
            {
                Log.e(TAG, "powerManager is null.");
                return;
            }
            if (wakeLock == null)
            {
                wakeLock = powerManager.newWakeLock(
                        PowerManager.PARTIAL_WAKE_LOCK, TAG);
                if (wakeLock == null)
                {
                    Log.e(TAG, "wakeLock is null.");
                    return;
                }
            }
            if (!wakeLock.isHeld())
            {
                wakeLock.acquire();
                if (!wakeLock.isHeld())
                {
                    Log.e(TAG, "Unable to hold wakeLock.");
                }
            }
        } catch (RuntimeException e)
        {
            Log.e(TAG, "Caught unexpected exception", e);
        }
    }

    /**
     * Releases the wake lock.
     */
    private void releaseWakeLock()
    {
        if (wakeLock != null && wakeLock.isHeld())
        {
            wakeLock.release();
            wakeLock = null;
        }
    }
    
    /** send notify where start or stop track*/
    private void showNotification()
    {
        if (mState == TRACK_STATE.STARTED)
        {
            Notification notification = new Notification(
                    R.drawable.ic_launcher, null, System.currentTimeMillis());
            PendingIntent contentIntent = PendingIntent.getActivity(this, 0,
                    new Intent(this, TrackActivity.class), 0);
            notification.setLatestEventInfo(this, getString(R.string.app_name),
                    "记录轨迹中...", contentIntent);
            notification.flags += Notification.FLAG_NO_CLEAR;
            startForeground(1, notification);
        } else
        {
            stopForeground(true);
        }
    }
}
